package xyz.lwm.lazycat.web.resolve;

import xyz.lwm.lazycat.beans.BeanDesign;
import xyz.lwm.lazycat.utility.It;
import xyz.lwm.lazycat.utility.Pair;
import xyz.lwm.lazycat.utility.PathUtil;
import xyz.lwm.lazycat.utility.reflect.AnnotationUtil;
import xyz.lwm.lazycat.utility.reflect.Annotations;
import xyz.lwm.lazycat.utility.reflect.ReflectUtil;
import xyz.lwm.lazycat.web.Controllable;
import xyz.lwm.lazycat.web.annocation.Controller;
import xyz.lwm.lazycat.web.annocation.Http;
import xyz.lwm.lazycat.web.annocation.Produces;
import xyz.lwm.lazycat.web.handle.HandlerType;
import xyz.lwm.lazycat.web.route.Router;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static xyz.lwm.lazycat.utility.StrPool.STR_EMPTY;

/**
 * controller 解析器
 *
 * @author lwm
 */
public class ControllerResolver implements Controllable {

    private final String keySuffix = UUID.randomUUID().toString();

    private final List<Pair<BeanDesign<?>, Object>> controllers = new ArrayList<>();

    void add(BeanDesign<?> design, Object controllerBean) {
        controllers.add(Pair.of(design, controllerBean));
    }

    @Override
    public void control(Router router) {
        for (Pair<BeanDesign<?>, Object> controller : controllers) {
            control(controller.getLeft(), controller.getRight(), router);
        }
    }

    /**
     * 解析控制器并注册路由
     *
     * @param design         design
     * @param controllerBean controller bean
     * @param router         router
     */
    private void control(BeanDesign<?> design, Object controllerBean, Router router) {
        // get http operations
        Map<String, Method> httpOperations = ReflectUtil.findDeclaredMethods(design.getType(), null, method -> {
            int modifiers = method.getModifiers();
            Annotations annotations = AnnotationUtil.getAnnotations(method);
            return Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && !Modifier.isAbstract(modifiers)
                    && !ReflectUtil.isObjectMethod(method) && annotations.hasAnnotation(Http.class);
        }, Method::toString, ControllerResolver.class.getName() + "#control(" + design.getName() + ")" + keySuffix);

        // resolve http operations
        Annotations annotations = design.getAnnotations();
        It<Produces> controllerProduces = It.of(annotations.getAnnotation(Produces.class));
        It<String> controllerPath = It.of(annotations.getAnnotation(Controller.class)).let(Controller::path);

        // resolve http operations
        for (Method operation : httpOperations.values()) {
            Annotations opeAnnotations = AnnotationUtil.getAnnotations(operation);
            // 获取请求信息
            It<Http> httpIt = It.of(opeAnnotations.getAnnotation(Http.class));

            // 获取请求方法
            HandlerType handlerType = httpIt.let(Http::handlerType)
                    .getOrThrow(() -> new IllegalArgumentException("Http.handlerType is null"));

            // 获取请求路径
            PathUtil.Pathable pathable = PathUtil.of(STR_EMPTY);
            if (controllerPath.isPresent()) {
                pathable = pathable.append(controllerPath.get());
            }
            It<String> pathIt = httpIt.let(Http::path);
            if (pathIt.isPresent()) {
                pathable = pathable.append(pathIt.get());
            }
            String path = pathable.toString();

            // 获取方法参数
            Parameter[] parameters = operation.getParameters();
            // 优先取方法上的注解, 其次取控制器类的注解
            It<Produces> produces = It.of(opeAnnotations.getAnnotation(Produces.class)).orElse(controllerProduces);

            // 创建方法处理器
            MethodHandler methodHandler = new MethodHandler(handlerType, controllerBean, operation, parameters, produces);

            // 注册路由
            switch (handlerType) {
                case GET:
                    router.get(path, methodHandler);
                    break;
                case POST:
                    router.post(path, methodHandler);
                    break;
                case PUT:
                    router.put(path, methodHandler);
                case PATCH:
                    router.patch(path, methodHandler);
                    break;
                case DELETE:
                    router.delete(path, methodHandler);
                    break;
                case HEAD:
                    router.head(path, methodHandler);
                    break;
            }

        }

    }

}
